/*
 * Copyright (c) 2016 Jean-Paul Etienne <fractalclone@gmail.com>
 * Contributors: 2018 Antmicro <www.antmicro.com>
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <toolchain.h>

/* exports */
GTEXT(__start)

/* imports */
GTEXT(__reset)
GTEXT(__irq_wrapper)

/*
 * following riscv32-qemu specs
 * IVT is placed at 0x000001000 and is mapped as follows:
 * 0x00001000: reset
 * 0x00001004: non-maskable interrupt (nmi) vector
 * 0x00001010: machine trap (mt) vector
 *
 * Call __irq_wrapper to handle all interrupts/exceptions/faults
 */
SECTION_FUNC(vectors, __start)
	.option norvc;

	/*
	 * jal instruction cannot be used to jump to address whose offset
	 * is > 12-bits wide. In this case, we have to use a call or tail
	 * instruction to jump to a far-away sub-routine.
	 *
	 * Given that IVT is found at a different address-space than the
	 * RAM in riscv32-qemu, we have to use call or tail instructions
	 * to jump to __reset or __isr_wrapper subroutines.
	 * However, call or tail instructions are pseudo instructions,
	 * which generate two base-instructions upon compilation. In this case,
	 * using them at a particular entry in the IVT will overwrite the next
	 * entry in the IVT. For example, using tail instruction in the
	 * reset vector, will overwrite the nmi-vector entry. To prevent this,
	 * perform a two-phase jump instructions to __reset or __irq_wrapper
	 * subroutines. The first jump performs a jal instruction, which will
	 * jump to an offset in the same vector address-space, but outside the
	 * IVT. The second jump performs a tail instruction to the __reset
	 * or __irq_wrapper subroutines.
	 */

	/* Call __reset for reset vector */
	jal x0, do_reset

	/* Call __irq_wrapper for nmi vector */
	jal x0, do_irq_wrapper

	.org 0x10
	/* Call __irq_wrapper for mt vector */
	jal x0, do_irq_wrapper

	.org 0x400 /* we are outside IVT */
do_reset:
	csrr    t0,mhartid
	bnez    t0, do_reset
	/*
	 * Set mtvec (Machine Trap-Vector Base-Address Register)
	 * to __irq_wrapper, so that we jump directly to __irq_wrapper,
	 * instead to the default machine trap vector address in IVT.
	 * This will preserve us from performing two jump instructions upon
	 * an interrupt.
	 */
	la t0, __irq_wrapper
	csrw mtvec, t0

	/* Jump to __reset */
	tail __reset

do_irq_wrapper:
	tail __irq_wrapper
